﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Office.Core;
using System.Windows.Forms;
using SharpSvn;
using System.IO;


namespace OfficeSVN
{
    public class SubversionController
    {
        private readonly ISubversion _subversion;
        private readonly IOfficeSVNRibbon _ribbon;
        private readonly IOfficePlugIn _plugIn;
        private string _currentDocument;

        private static readonly Dictionary<string, SubversionFileInfo> SvnInfoCache = new Dictionary<string, SubversionFileInfo>();

        public SubversionController(IOfficePlugIn plugIn)
        {
            _plugIn = plugIn;
            _plugIn.DocumentChanged += PlugInDocumentChanged;

            _ribbon = new OfficeSvnRibbon();
            _ribbon.ButtonClicked += RibbonButtonClicked;
            _ribbon.DocumentSaved += RibbonDocumentSaved;
            
            _subversion = new Subversion();
        }

        protected SubversionController(IOfficePlugIn plugIn, IOfficeSVNRibbon ribbon, ISubversion subversion)
        {
            _plugIn = plugIn;
            _ribbon = ribbon;
            _subversion = subversion;
        }

        void RibbonDocumentSaved(object sender, DocumentEventArgs e)
        {
            _plugIn.SaveDocument(e.Document);

            InvalidateCache(_currentDocument);

            _ribbon.UpdateRibbon(_currentDocument, this.GetInfo(_currentDocument));
        }

        private static void InvalidateCache(string path)
        {
            if (SvnInfoCache.ContainsKey(path))
            {
                SvnInfoCache.Remove(path);
            }
        }

        private void RibbonButtonClicked(object sender, RibbonButtonEventArgs e)
        {
            // TODO: Eigene EventArgs, damit keine Abhängigkeit zu buttonId´s besteht
            switch (e.ButtonId)
            {
                case "updateButton":
                    this.Update(_currentDocument);
                    break;

                case "commitButton":
                    this.Commit(_currentDocument);
                    break;

                case "revertButton":
                    this.Revert(_currentDocument);
                    break;

                case "addButton":
                    this.AddToSubversion(_currentDocument);
                    break;

                case "releaseLockButton":
                    this.ReleaseLock(_currentDocument);
                    break;

                case "getLockButton":
                    this.GetLock(_currentDocument);
                    break;
            }
        }

        private void PlugInDocumentChanged(object sender, DocumentEventArgs e)
        {
            _currentDocument = e.Document;

            //InvalidateCache(_currentDocument);

            _ribbon.UpdateRibbon(_currentDocument, this.GetInfo(_currentDocument));
        }

        private SubversionFileInfo GetInfo(string path) {

            SubversionFileInfo returnValue = new SubversionFileInfo();

            // Stop if file does not exist
            if (!File.Exists(path))
                return returnValue;

            // Look in the cache
            if (SvnInfoCache.ContainsKey(path))
                return SvnInfoCache[path];

            returnValue = _subversion.GetInfo(path);

            // Add info to cache
            SvnInfoCache.Add(path, returnValue);

            return returnValue;
        }

        /// <summary>
        /// Gets the lock for the passed file.
        /// </summary>
        /// <param name="path"></param>
        private void GetLock(string path)
        {
            _subversion.GetLock(path);

            InvalidateCache(path);

            _plugIn.ReloadDocument(path);
        }

        /// <summary>
        /// Release the lock for the passed file.
        /// </summary>
        /// <param name="path"></param>
        private void ReleaseLock(string path)
        {
            _subversion.ReleaseLock(path);

            InvalidateCache(path);

            _plugIn.ReloadDocument(path);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="comment"></param>
        private void Commit(string path)
        {

            SvnCommitForm CommitForm = new SvnCommitForm();
            if (CommitForm.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            // Force to close the document
            _plugIn.CloseDocument(path);

            _subversion.Commit(path, CommitForm.CommitMessage);

            InvalidateCache(path);

            // Open document again
            _plugIn.OpenDocument(path);
        }

        /// <summary>
        /// Update the passed file to the latest revision.
        /// </summary>
        /// <param name="path"></param>
        private void Update(string path)
        {

            // Idea: Check if a newer version exists. In case the working copy is up2date, than we don´t need the update...
            if (_subversion.IsUpdateAvailable(path))
            {

                // Force to close the document
                _plugIn.CloseDocument(path);

                _subversion.Update(path);

                InvalidateCache(path);

                // Open document again
                _plugIn.OpenDocument(path);
            }
        }

        /// <summary>
        /// Revert the file and discard the changes.
        /// </summary>
        /// <param name="path"></param>
        private void Revert(string path)
        {

            if (_subversion.GetStatus(path) == SvnStatus.Added)
            {
                // Undo add
                _subversion.Revert(path);

                InvalidateCache(path);

                _ribbon.UpdateRibbon(path, this.GetInfo(path));

            }
            else
            {
                // TODO: -> view
                if (MessageBox.Show("Do you really want to revert your changes?", "OfficeSVN", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No)
                {
                    return;
                }

                // Force to close the document
                _plugIn.CloseDocument(path);

                // Revert changes
                _subversion.Revert(path);

                InvalidateCache(path);

                // Open document again
                _plugIn.OpenDocument(path);
            }
        }

        /// <summary>
        /// Add the passed file to subversion.
        /// </summary>
        /// <param name="path"></param>
        private void AddToSubversion(string path)
        {

            // Force to close the document
            _plugIn.CloseDocument(path);

            _subversion.AddFile(path);

            InvalidateCache(path);

            // Open document again
            _plugIn.OpenDocument(path);

            //try {

            //    // Add document to subversion
            //    SvnAddArgs Args = new SvnAddArgs();
            //    client.Add(path, Args);

            //    UpdateRibbon(null);

            //} catch (SvnAuthorizationException) {

            //    /* 
            //     * This exception occurs if the document is still locked.
            //     * Force to close the document, add the document and
            //     * open it again.
            //     */

            //    // Force to close the document
            //    OnCloseDocumentEvent(path);

            //    // Add document to subversion
            //    SvnAddArgs Args = new SvnAddArgs();
            //    client.Add(path, Args);

            //    // Open document again
            //    OnOpenDocumentEvent(path);

            //} catch (SvnException exception) {
            //    HandleSvnException(exception);
            //}
        }

        public IRibbonExtensibility GetRibbon()
        {
            return _ribbon;
        }
    }
}
